package com.virtualightning.mvvm.widget

import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.*
import androidx.core.view.ViewCompat
import com.virtualightning.mvvm.R
import com.virtualightning.mvvm.extensions.findViewIndex
import com.virtualightning.mvvm.extensions.forEach
import com.virtualightning.mvvm.tools.shapes.VLPoint
import com.virtualightning.mvvm.tools.shapes.VLRange
import com.virtualightning.mvvm.tools.touch.SingleTouchHelper

/**
 *  Anchor : Create by CimZzz
 *  Time : 2021/12/16 14:20
 *  嵌套布局
 */
class NestedLayout : ViewGroup {
	constructor(context: Context?) : super(context)
	constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
	constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
		context,
		attrs,
		defStyleAttr
	)

	/**
	 * 滑动范围
	 */
	private var scrollRange: VLRange = VLRange()

	/**
	 * 触控助手
	 */
	private val touchHelper: SingleTouchHelper = SingleTouchHelper()

	/**
	 * 辅助点
	 */
	private val helperPoint: VLPoint = VLPoint()

	/**
	 * 当前滑动坐标点
	 */
	private val currentPoint: VLPoint = VLPoint()

	/**
	 * 内部嵌套滑动 View
	 */
	private var innerScrollView: View? = null

	/**
	 * 判断当前是否已经到达边界
	 */
	private var isBound: Boolean = false

	init {
		val configuration = ViewConfiguration.get(context)
		touchHelper.setYSlop(configuration.scaledTouchSlop)

		currentPoint.reset()
	}

	override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
		super.addView(child, index, params)
		Log.v("CimZzz", "addView3: ${child is ViewParent}")
	}

	override fun generateLayoutParams(attrs: AttributeSet): ViewGroup.LayoutParams {
		return LayoutParams(context, attrs)
	}

	override fun generateLayoutParams(p: ViewGroup.LayoutParams): ViewGroup.LayoutParams {
		return LayoutParams(p.width, p.height)
	}

	override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams {
		return LayoutParams(
			ViewGroup.LayoutParams.WRAP_CONTENT,
			ViewGroup.LayoutParams.WRAP_CONTENT
		)
	}

	override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec)

		var remainedHeight = measuredHeight

		var contentHeight = 0
		forEach { child ->
			if (child.visibility != View.GONE) {
				val lp = child.layoutParams as LayoutParams
				measureChild(child, widthMeasureSpec, heightMeasureSpec)
				if (lp.behaviorFlag == LayoutParams.BehaviorFlag_Fix) {
					remainedHeight -= child.measuredHeight
				}
				contentHeight += child.measuredHeight + lp.topMargin + lp.bottomMargin
			}
		}

		if (measuredHeight < contentHeight) {
			scrollRange.set(0f, (contentHeight - measuredHeight).toFloat())
		} else {
			scrollRange.set(0f, 0f)
		}
		currentPoint.yInRange(scrollRange)
	}

	override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
		var top = t
		forEach { child ->
			val lp = child.layoutParams as LayoutParams
			val realTop = top + lp.topMargin
			val realBottom = realTop + child.measuredHeight
			lp.scrollTopBound = realTop.toFloat()
			lp.scrollBottomBound = realBottom.toFloat()
			child.layout(l, realTop, r, realBottom)
			top = realBottom + lp.bottomMargin
		}
	}

	///////////////////////////////////////////////////////////////////////////
	// 代码分割线 on 2021/12/21 11:08 上午 by CimZzz
	// 以下内容为:
	// 触控滑动方法
	///////////////////////////////////////////////////////////////////////////

	override fun onStartNestedScroll(child: View?, target: View?, nestedScrollAxes: Int): Boolean {
		return nestedScrollAxes and ViewCompat.SCROLL_AXIS_VERTICAL != 0
	}

	override fun onNestedPreScroll(target: View?, dx: Int, dy: Int, consumed: IntArray) {
		if(innerScrollView == target) {
			return
		}
		if (isBound) {
			return
		}
		helperPoint.set(currentPoint)
		currentPoint.offsetY(dy.toFloat())
		currentPoint.yInRange(scrollRange)
		val useY = currentPoint.distanceY(helperPoint)
		if (useY != 0f) {
			consumed[1] = useY.toInt()
			scrollTo(0, currentPoint.y.toInt())
		}
		if (currentPoint.y == scrollRange.getMax()) {
			isBound = true
		}
	}

	override fun onNestedScroll(
		target: View?,
		dxConsumed: Int,
		dyConsumed: Int,
		dxUnconsumed: Int,
		dyUnconsumed: Int
	) {
		if (dyUnconsumed != 0) {
			helperPoint.set(currentPoint)
			currentPoint.offsetY(dyUnconsumed.toFloat())
			currentPoint.yInRange(scrollRange)
			val useY = currentPoint.distanceY(helperPoint)
			if (useY != 0f) {
				scrollTo(0, currentPoint.y.toInt())
				isBound = false
			}
		}
	}

	override fun onNestedPreFling(target: View?, velocityX: Float, velocityY: Float): Boolean {
		Log.v("CimZzz", "onNestedPreFling: $velocityY")
		return super.onNestedPreFling(target, velocityX, velocityY)
	}

	override fun onNestedFling(
		target: View?,
		velocityX: Float,
		velocityY: Float,
		consumed: Boolean
	): Boolean {
		Log.v("CimZzz", "onNestedFling: $velocityY, $consumed")
		return super.onNestedFling(target, velocityX, velocityY, consumed)
	}

	@SuppressLint("ClickableViewAccessibility")
	override fun onTouchEvent(event: MotionEvent): Boolean {
		if (event.action == MotionEvent.ACTION_DOWN) {
			touchHelper.downAt(event)
			return ViewCompat.startNestedScroll(this, ViewCompat.SCROLL_AXIS_VERTICAL)
		}

		touchHelper.moveTo(event, true)

		return true
	}


	private fun innerScrollTo(y: Float): Float {
		if (scrollRange.isInvalid()) {
			return 0f
		}
		// 先判断滑动方向
		var index = findCurrentTopViewIndex()
		if (index == -1) {
			return 0f
		}
		var doMoving = false
		var dy = y
		val currentTop = currentPoint.y
		var currentView = getChildAt(index)
		val isUp = dy < 0
		while (dy != 0f) {
			val currentParams = currentView.layoutParams as LayoutParams
			if (isUp) {
				// todo 向上滑动
				val topDiff = currentParams.scrollTopBound - currentTop
				if (topDiff <= dy) {
					// 没有达到顶部，先补齐到顶部
					currentPoint.offsetY(dy)
					doMoving = true
					break
				}
				if (topDiff != 0f) {
					// 没有达到顶部，并且当前偏移量超出需要补齐到顶部的偏移量
					// 先补齐到顶部，然后向其转发嵌套滑动
					currentPoint.offsetY(topDiff)
					doMoving = true
					dy -= topDiff
				}

				if(currentView.canScrollVertically(-1)) {
					innerScrollView = currentView
					if(doMoving) {
						doMoving = false
						executeMoving()
					}
					currentView.scrollBy(0, dy.toInt())
					dy = 0f
					break
				}

				val topMargin = currentParams.topMargin
				if(topMargin != 0) {
					// 补齐上边距
					val marginDiff = dy.coerceAtMost(-topMargin.toFloat())
					currentPoint.offsetY(marginDiff)
					doMoving = true
				}

				currentView = findView(-- index) ?: break
			} else {
				// todo 向下滑动
			}
		}

		if(doMoving) {
			executeMoving()
		}
		return y - dy
	}

	/**
	 * 找到上一个 View
	 * @param index Int 下标
	 * @return View? 上一个 View
	 */
	private fun findView(index: Int): View? {
		if(index == 0 || index >= childCount) {
			return null
		}
		return getChildAt(index)
	}

	/**
	 * 找到当前顶部 View
	 * @return View 顶部 View
	 */
	private fun findCurrentTopViewIndex(): Int {
		val currentTop = currentPoint.y
		return findViewIndex { view ->
			val lp = view.layoutParams as LayoutParams
			lp.scrollBottomBound >= currentTop
		}
	}



	/**
	 * 执行滑动
	 */
	private fun executeMoving() {
		scrollTo(0, currentPoint.y.toInt())
	}

	///////////////////////////////////////////////////////////////////////////
	// 代码分割线 on 2021/12/16 2:22 下午 by CimZzz
	// 以下内容为:
	// 内部类
	///////////////////////////////////////////////////////////////////////////

	/**
	 * 布局参数
	 */
	class LayoutParams : MarginLayoutParams {
		companion object {
			/**
			 * 允许滑动
			 */
			const val BehaviorFlag_Scroll = 0

			/**
			 * 固定
			 */
			const val BehaviorFlag_Fix = 1
		}

		constructor(width: Int, height: Int) : super(width, height)
		constructor(c: Context, attrs: AttributeSet) : super(c, attrs) {
			val typedArray = c.obtainStyledAttributes(attrs, R.styleable.NestedLayout_Layout)
			behaviorFlag =
				typedArray.getInt(R.styleable.NestedLayout_Layout_behaviorFlags, behaviorFlag)
			typedArray.recycle()
		}

		/**
		 * 行为类型，默认为滑动
		 */
		internal var behaviorFlag: Int = BehaviorFlag_Scroll

		/**
		 * 顶部滑动边界
		 */
		internal var scrollTopBound: Float = 0f

		/**
		 * 底部滑动边界
		 */
		internal var scrollBottomBound: Float = 0f
	}


}